<script>
    import fecha from 'element-ui/src/utils/date';
    import {
        range as rangeArr,
        getFirstDayOfMonth,
        getPrevMonthLastDays,
        getMonthDays,
        getI18nSettings,
        validateRangeInOneMonth
    } from 'element-ui/src/utils/date-util';

    export default {
        props: {
            selectedDay: String, // formated date yyyy-MM-dd
            range: {
                type: Array,
                validator(val) {
                    if (!(val && val.length)) return true;
                    const [start, end] = val;
                    return validateRangeInOneMonth(start, end);
                }
            },
            date: Date,
            hideHeader: Boolean,
            firstDayOfWeek: Number
        },

        inject: ['elCalendar'],

        data() {
            return {
                WEEK_DAYS: getI18nSettings().dayNames
            };
        },

        methods: {
            toNestedArr(days) {
                return rangeArr(days.length / 7).map((_, index) => {
                    const start = index * 7;
                    return days.slice(start, start + 7);
                });
            },

            getFormateDate(day, type) {
                if (!day || ['prev', 'current', 'next'].indexOf(type) === -1) {
                    throw new Error('invalid day or type');
                }
                let prefix = this.curMonthDatePrefix;
                if (type === 'prev') {
                    prefix = this.prevMonthDatePrefix;
                } else if (type === 'next') {
                    prefix = this.nextMonthDatePrefix;
                }
                day = `00${day}`.slice(-2);
                return `${prefix}-${day}`;
            },

            getCellClass({text, type}) {
                const classes = [type];
                if (type === 'current') {
                    const date = this.getFormateDate(text, type);
                    if (date === this.selectedDay) {
                        classes.push('is-selected');
                    }
                    if (date === this.formatedToday) {
                        classes.push('is-today');
                    }
                }
                return classes;
            },

            pickDay({text, type}) {
                const date = this.getFormateDate(text, type);
                this.$emit('pick', date);
            },

            cellRenderProxy({text, type}) {
                let render = this.elCalendar.$scopedSlots.dateCell;
                if (!render) return
            <
                span > {text} < /span>;

                const day = this.getFormateDate(text, type);
                const date = new Date(day);
                const data = {
                    isSelected: this.selectedDay === day,
                    type: `${type}-month`,
                    day
                };
                return render({date, data});
            }
        },

        computed: {
            prevMonthDatePrefix() {
                const temp = new Date(this.date.getTime());
                temp.setDate(0);
                return fecha.format(temp, 'yyyy-MM');
            },

            curMonthDatePrefix() {
                return fecha.format(this.date, 'yyyy-MM');
            },

            nextMonthDatePrefix() {
                const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
                return fecha.format(temp, 'yyyy-MM');
            },

            formatedToday() {
                return this.elCalendar.formatedToday;
            },

            isInRange() {
                return this.range && this.range.length;
            },

            rows() {
                let days = [];
                // if range exists, should render days in range.
                if (this.isInRange) {
                    const [start, end] = this.range;
                    const currentMonthRange = rangeArr(end.getDate() - start.getDate() + 1).map((_, index) => ({
                        text: start.getDate() + index,
                        type: 'current'
                    }));
                    let remaining = currentMonthRange.length % 7;
                    remaining = remaining === 0 ? 0 : 7 - remaining;
                    const nextMonthRange = rangeArr(remaining).map((_, index) => ({
                        text: index + 1,
                        type: 'next'
                    }));
                    days = currentMonthRange.concat(nextMonthRange);
                } else {
                    const date = this.date;
                    let firstDay = getFirstDayOfMonth(date);
                    firstDay = firstDay === 0 ? 7 : firstDay;
                    const firstDayOfWeek = typeof this.firstDayOfWeek === 'number' ? this.firstDayOfWeek : 1;
                    const prevMonthDays = getPrevMonthLastDays(date, firstDay - firstDayOfWeek).map(day => ({
                        text: day,
                        type: 'prev'
                    }));
                    const currentMonthDays = getMonthDays(date).map(day => ({
                        text: day,
                        type: 'current'
                    }));
                    days = [...prevMonthDays, ...currentMonthDays];
                    const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
                        text: index + 1,
                        type: 'next'
                    }));
                    days = days.concat(nextMonthDays);
                }
                return this.toNestedArr(days);
            },

            weekDays() {
                const start = this.firstDayOfWeek;
                const {WEEK_DAYS} = this;

                if (typeof start !== 'number' || start === 0) {
                    return WEEK_DAYS.slice();
                } else {
                    return WEEK_DAYS.slice(start).concat(WEEK_DAYS.slice(0, start));
                }
            }
        },

        render() {
            const thead = this.hideHeader ? null : ( < thead >
                {
                    this.weekDays.map(day => < th
            key = {day} > {day} < /th>)
        }
        <
            /thead>);
            return (
                < table
        class
            = {
            {
                'el-calendar-table'
            :
                true,
                    'is-range'
            :
                this.isInRange
            }
        }
            cellspacing = "0"
            cellpadding = "0" >
                {
                    thead
                }
                < tbody >
                {
                    this.rows.map((row, index) => < tr
        class
            = {
            {
                'el-calendar-table__row'
            :
                true,
                    'el-calendar-table__row--hide-border'
            :
                index === 0 && this.hideHeader
            }
        }
            key = {index} >
                {
                    row.map((cell, key) => < td
            key = {key}
        class
            = {this.getCellClass(cell)
        }
            onClick = {this.pickDay.bind(this, cell)
        }>
        <
            div
        class
            = "el-calendar-day" >
                {
                    this.cellRenderProxy(cell)
        }
        <
            /div>
            < /td>)
        }
        <
            /tr>)
        }
        <
            /tbody>
            < /table>);
        }
    };
</script>
